//----------------------------------------------------------------
//module name : yhz_riscv64i
//engineer : yhz
//date : 2021.07.28
//----------------------------------------------------------------
`include "yhz_defines.v"
module yhz_riscv64i (
    input  wire        i_clk                    ,
    input  wire        i_rst                    ,
    //IF
    input  wire [63:0] i_r_inst_data            ,
    input  wire        i_r_inst_ready           ,
    output wire        o_r_inst_valid           ,
    output wire [63:0] o_r_inst_addr            ,
    //RAM
    input  wire        i_w_ram_ready            ,
    output wire        o_w_ram_valid            ,
    output wire [63:0] o_w_ram_addr             ,
    output wire [63:0] o_w_ram_data             ,
    output wire [63:0] o_w_ram_mask             ,
    input  wire        i_r_ram_ready            ,
    output wire        o_r_ram_valid            ,
    output wire [63:0] o_r_ram_addr             ,
    input  wire [63:0] i_r_ram_data             ,
    //difftest
    output wire        diff_uart_out_valid      ,
    output wire [7:0]  diff_uart_out_ch         ,

    output wire [63:0] diff_pc_addr             ,
    output wire [31:0] diff_instruction         ,
    output wire        diff_pipeline_pulse      ,
    output wire        diff_clint_mem           ,
    output wire        diff_w_rd_en             ,
    output wire [4:0]  diff_w_rd_addr           ,
    output wire [63:0] diff_w_rd_data           ,
    output wire        diff_trap_ecall          ,
    output wire        diff_trap_timer          ,

    output wire [63:0] diff_mstatus             ,
    output wire [63:0] diff_mepc                ,
    output wire [63:0] diff_mtvec               ,
    output wire [63:0] diff_mcause              ,
    output wire [63:0] diff_mip                 ,
    output wire [63:0] diff_mie                 ,
    output wire [63:0] diff_mscratch            ,
    output wire [63:0] diff_reg_difftest [31:0]  
);
//----------------------------------------------------------------
//register & wire
//----------------------------------------------------------------
    //ram
    wire        pipeline_pulse_t1 ;
    wire        pipeline_pulse_t2 ;
    wire        pipeline_pulse_t3 ;
    wire        pipeline_unlock   ;
    wire        hand_shake_flag   ;
    wire [63:0] pc_addr           ;
    wire [31:0] instruction       ;
    //trap
    wire        trap_ecall        ;
    wire        trap_timer        ;
    wire        trap              ;
    wire        mret              ;
    wire [63:0] mtvec_addr        ;
    wire [63:0] mepc_addr         ;
    //jump
    wire        auipc_en          ;
    wire        jal_en            ;
    wire        jalr_en           ;
    wire        branch_en         ;
    wire [63:0] pcj_addr          ;
    //csr
    wire        w_csr_en          ;
    wire [11:0] w_csr_addr        ;
    wire [63:0] w_csr_data        ;
    wire        r_csr_en          ;
    wire [11:0] r_csr_addr        ;
    wire [63:0] r_csr_data        ;
    //rd
    wire        r_rs1_en          ;
    wire        r_rs2_en          ;
    wire [4:0]  r_rs1_addr        ;
    wire [4:0]  r_rs2_addr        ;
    wire [63:0] r_rs1_data        ;
    wire [63:0] r_rs2_data        ;

    wire [63:0] inst_op1          ;
    wire [63:0] inst_op2          ;
    wire [63:0] inst_op3          ;
    wire [7:0]  inst_encode       ;

    wire        load_en_t         ;
    wire        load_en           ;
    wire        store_en          ;
    wire [4:0]  load_cmd          ;
    wire [63:0] store_data        ;
    wire [63:0] store_mask        ;

    wire        w_rd_en_t1        ;
    wire        w_rd_en_t2        ;
    wire        w_rd_en_t3        ;
    wire        w_rd_en_t4        ;
    wire [4:0]  w_rd_addr_t1      ;
    wire [4:0]  w_rd_addr_t2      ;
    wire [4:0]  w_rd_addr_t3      ;
    wire [4:0]  w_rd_addr_t4      ;
    wire [63:0] w_rd_data_t1      ;
    wire [63:0] w_rd_data_t2      ;
    wire [63:0] w_rd_data_em      ;
    wire [63:0] w_rd_data_t3      ;
    wire [63:0] w_rd_data_t4      ;
    //clint
    wire        w_mtime_en        ;
    wire        r_mtime_en        ;
    wire        w_mtimecmp_en     ;
    wire        r_mtimecmp_en     ;
    wire [63:0] r_clint_data      ;
//----------------------------------------------------------------
//module
//----------------------------------------------------------------
    yhz_instruction_fetch IF(
        .i_clk             (i_clk            ),
        .i_rst             (i_rst            ),
        .i_pipeline_pulse  (pipeline_pulse_t1),
        .o_hand_shake_flag (hand_shake_flag  ),
        //trap
        .i_trap            (trap             ),
        .i_mret            (mret             ),
        .i_mtvec_addr      (mtvec_addr       ),
        .i_mepc_addr       (mepc_addr        ),
        //jump
        .i_jal_en          (jal_en           ),
        .i_jalr_en         (jalr_en          ),
        .i_branch_en       (branch_en        ),
        .i_pcj_addr        (pcj_addr         ),
        //to_AXI
        .i_r_inst_data     (i_r_inst_data    ),
        .i_r_inst_ready    (i_r_inst_ready   ),
        .o_r_inst_valid    (o_r_inst_valid   ),
        .o_r_inst_addr     (o_r_inst_addr    ),
        //to_instruction_decode
        .o_pc_addr         (pc_addr          ),
        .o_instruction     (instruction      ) 
    );

    yhz_instruction_decode ID(
        .i_clk               (i_clk              ),
        .i_rst               (i_rst              ),
        .i_pipeline_unlock   (pipeline_unlock    ),
        .o_pipeline_pulse    (pipeline_pulse_t1  ),
        //transfer
        .i_pc_addr           (pc_addr            ),
        .i_instruction       (instruction        ),
        .i_rs1_data          (r_rs1_data         ),
        .i_rs2_data          (r_rs2_data         ),
        .i_r_csr_data        (r_csr_data         ),

        .i_w_rd_en_exe       (w_rd_en_t2         ),
        .i_w_rd_en_mem       (w_rd_en_t3         ),
        .i_w_rd_en_wb        (w_rd_en_t4         ),
        .i_w_rd_addr_exe     (w_rd_addr_t2       ),
        .i_w_rd_addr_mem     (w_rd_addr_t3       ),
        .i_w_rd_addr_wb      (w_rd_addr_t4       ),
        .i_w_rd_data         (w_rd_data_t1       ),
        .i_w_rd_data_exe     (w_rd_data_t2       ),
        .i_w_rd_data_mem     (w_rd_data_t3       ),
        .i_w_rd_data_wb      (w_rd_data_t4       ),
        
        .i_trap_timer        (trap_timer         ),
        .o_trap_ecall        (trap_ecall         ),
        .o_mret              (mret               ),

        .o_jal_en            (jal_en             ),
        .o_jalr_en           (jalr_en            ),
        .o_branch_en         (branch_en          ),
        .o_pcj_addr          (pcj_addr           ),

        .o_r_rs1_en          (r_rs1_en           ),
        .o_r_rs2_en          (r_rs2_en           ),
        .o_r_csr_en          (r_csr_en           ),
        .o_r_rs1_addr        (r_rs1_addr         ),
        .o_r_rs2_addr        (r_rs2_addr         ),
        .o_r_csr_addr        (r_csr_addr         ),
        //to_execute
        .o_w_rd_en           (w_rd_en_t1         ),
        .o_w_csr_en          (w_csr_en           ),
        .o_w_rd_addr         (w_rd_addr_t1       ),
        .o_w_csr_addr        (w_csr_addr         ),
        .o_w_csr_data        (w_csr_data         ),
        .o_inst_op1          (inst_op1           ),
        .o_inst_op2          (inst_op2           ),
        .o_inst_op3          (inst_op3           ),
        .o_inst_encode       (inst_encode        ),
        //difftest
        .diff_uart_out_valid (diff_uart_out_valid),
        .diff_uart_out_ch    (diff_uart_out_ch   ) 
    );

    yhz_execute EXE(
        .i_clk             (i_clk            ),
        .i_rst             (i_rst            ),
        .i_pipeline_unlock (pipeline_unlock  ),
        .i_pipeline_pulse  (pipeline_pulse_t1),
        .o_pipeline_pulse  (pipeline_pulse_t2),
        //command
        .i_inst_encode     (inst_encode      ),
        .i_inst_op1        (inst_op1         ),
        .i_inst_op2        (inst_op2         ),
        .i_inst_op3        (inst_op3         ),
        //transfer
        .i_w_rd_en         (w_rd_en_t1       ),
        .i_w_rd_addr       (w_rd_addr_t1     ),
        .o_w_rd_data_t     (w_rd_data_t1     ),
        //to_memory
        .o_load_en         (load_en          ),
        .o_store_en        (store_en         ),
        .o_load_cmd        (load_cmd         ),
        .o_store_data      (store_data       ),
        .o_store_mask      (store_mask       ),
        .o_w_rd_en         (w_rd_en_t2       ),
        .o_w_rd_addr       (w_rd_addr_t2     ),
        .o_w_rd_data       (w_rd_data_em     ) 
    );

    yhz_memory MEM(
        .i_clk             (i_clk            ),
        .i_rst             (i_rst            ),
        .i_pipeline_pulse  (pipeline_pulse_t2),
        .o_pipeline_unlock (pipeline_unlock  ),
        .o_pipeline_pulse  (pipeline_pulse_t3),
        //command
        .i_load_en         (load_en          ),
        .i_store_en        (store_en         ),
        .i_load_cmd        (load_cmd         ),
        .i_store_data      (store_data       ),
        .i_store_mask      (store_mask       ),
        //transfer
        .i_w_rd_en         (w_rd_en_t2       ),
        .i_w_rd_addr       (w_rd_addr_t2     ),
        .i_w_rd_data       (w_rd_data_em     ),
        .o_w_rd_data_t     (w_rd_data_t2     ),
        //to_write_back
        .o_w_rd_en         (w_rd_en_t3       ),
        .o_w_rd_addr       (w_rd_addr_t3     ),
        .o_w_rd_data       (w_rd_data_t3     ),
        //clint
        .o_w_mtime_en      (w_mtime_en       ),
        .o_r_mtime_en      (r_mtime_en       ),
        .o_w_mtimecmp_en   (w_mtimecmp_en    ),
        .o_r_mtimecmp_en   (r_mtimecmp_en    ),
        .i_r_clint_data    (r_clint_data     ),
        //to_RAM
        .i_w_ram_ready     (i_w_ram_ready    ),
        .o_w_ram_valid     (o_w_ram_valid    ),
        .o_w_ram_addr      (o_w_ram_addr     ),
        .o_w_ram_data      (o_w_ram_data     ),
        .o_w_ram_mask      (o_w_ram_mask     ),
        .i_r_ram_ready     (i_r_ram_ready    ),
        .o_r_ram_valid     (o_r_ram_valid    ),
        .o_r_ram_addr      (o_r_ram_addr     ),
        .i_r_ram_data      (i_r_ram_data     ) 
    );

    yhz_write_back WB(
        .i_clk             (i_clk            ),
        .i_rst             (i_rst            ),
        .i_pipeline_unlock (pipeline_unlock  ),
        .i_pipeline_pulse  (pipeline_pulse_t3),
        //transfer
        .i_w_rd_en         (w_rd_en_t3       ),
        .i_w_rd_addr       (w_rd_addr_t3     ),
        .i_w_rd_data       (w_rd_data_t3     ),
        //to_common_register
        .o_w_rd_en         (w_rd_en_t4       ),
        .o_w_rd_addr       (w_rd_addr_t4     ),
        .o_w_rd_data       (w_rd_data_t4     ) 
    );

    yhz_common_register CR(
        .i_clk          (i_clk            ),
        .i_rst          (i_rst            ),
        //rd
        .i_w_rd_en      (w_rd_en_t4       ),
        .i_w_rd_addr    (w_rd_addr_t4     ),
        .i_w_rd_data    (w_rd_data_t4     ),
        //rs
        .i_r_rs1_en     (r_rs1_en         ),
        .i_r_rs2_en     (r_rs2_en         ),
        .i_r_rs1_addr   (r_rs1_addr       ),
        .i_r_rs2_addr   (r_rs2_addr       ),
        .o_r_rs1_data   (r_rs1_data       ),
        .o_r_rs2_data   (r_rs2_data       ),
        //difftest
        .o_reg_difftest (diff_reg_difftest) 
    );

    yhz_control_and_state_register CSR(
        .i_clk             (i_clk           ),
        .i_rst             (i_rst           ),
        .i_hand_shake_flag (hand_shake_flag ),
        //trap
        .i_trap_ecall      (trap_ecall      ),
        .o_trap_timer      (trap_timer      ),
        .o_trap            (trap            ),
        .i_mret            (mret            ),
        .i_pc_addr         (pc_addr         ),
        .o_mtvec_addr      (mtvec_addr      ),
        .o_mepc_addr       (mepc_addr       ),
        //clint
        .i_w_mtime_en      (w_mtime_en      ),
        .i_r_mtime_en      (r_mtime_en      ),
        .i_w_mtimecmp_en   (w_mtimecmp_en   ),
        .i_r_mtimecmp_en   (r_mtimecmp_en   ),
        .i_w_clint_data    (o_w_ram_data    ),
        .o_r_clint_data    (r_clint_data    ),
        //write
        .i_w_csr_en        (w_csr_en        ),
        .i_w_csr_addr      (w_csr_addr      ),
        .i_w_csr_data      (w_csr_data      ),
        //read
        .i_r_csr_en        (r_csr_en        ),
        .i_r_csr_addr      (r_csr_addr      ),
        .o_r_csr_data      (r_csr_data      ),
        //difftest
        .diff_mstatus      (diff_mstatus    ),
        .diff_mepc         (diff_mepc       ),
        .diff_mtvec        (diff_mtvec      ),
        .diff_mcause       (diff_mcause     ),
        .diff_mip          (diff_mip        ),
        .diff_mie          (diff_mie        ),
        .diff_mscratch     (diff_mscratch   ) 
    );
//----------------------------------------------------------------
//difftest
//----------------------------------------------------------------
    //difftest
    assign diff_pc_addr        = pc_addr                                                 ;
    assign diff_instruction    = instruction                                             ;
    assign diff_pipeline_pulse = pipeline_pulse_t1 & (!pipeline_unlock)                  ;
    assign diff_clint_mem      = w_mtime_en | r_mtime_en | w_mtimecmp_en | r_mtimecmp_en ;
    assign diff_w_rd_en        = w_rd_en_t4                                              ;
    assign diff_w_rd_addr      = w_rd_addr_t4                                            ;
    assign diff_w_rd_data      = w_rd_data_t4                                            ;
    assign diff_trap_ecall     = trap_ecall                                              ;
    assign diff_trap_timer     = trap_timer & hand_shake_flag                            ;
//----------------------------------------------------------------
endmodule
//----------------------------------------------------------------
